home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Cream of the Crop 1
/
Cream of the Crop 1.iso
/
COMM
/
MSKRMSRC.ARJ
/
MSNDNS.C
< prev
next >
Wrap
C/C++ Source or Header
|
1991-10-24
|
16KB
|
577 lines
/* File MSNDNS.C
* Domain name server requester
*
* Copyright (C) 1991, University of Waterloo.
* Copyright (C) 1991, Trustees of Columbia University in the
* City of New York. Permission is granted to any individual or
* institution to use, copy, or redistribute this software as long as
* it is not sold for profit and this copyright notice is retained.
*
* Original version created by Erick Engelke of the University of
* Waterloo, Waterloo, Ontario, Canada.
* Adapted and modified for MS-DOS Kermit by Joe R. Doupnik,
* Utah State University, jrd@cc.usu.edu, jrd@usu.Bitnet.
*
* Last edit
* 6 Sept 1991
*
* Originally based on NCSA Telnet
*
*/
#include "msntcp.h"
#include "msnlib.h"
byte *def_domain;
byte *loc_domain = NULL;
/* current subname to be used by domain system */
longword def_nameservers[ MAX_NAMESERVERS ];
int last_nameserver;
static udp_Socket *dom_sock;
#define DOMSIZE 512 /* maximum domain message size to mess with */
/*
* Header for the DOMAIN queries
* ALL OF THESE ARE BYTE SWAPPED QUANTITIES!
*/
struct dhead {
word ident, /* unique identifier */
flags,
qdcount, /* question section, # of entries */
ancount, /* answers, how many */
nscount, /* count of name server Respsonse Records */
arcount; /* number of "additional" records */
};
/*
* flag masks for the flags field of the DOMAIN header
*/
#define DQR 0x8000 /* query = 0, response = 1 */
#define DOPCODE 0x7100 /* opcode, see below */
#define DAA 0x0400 /* Authoritative answer */
#define DTC 0x0200 /* Truncation, response was cut off at 512 */
#define DRD 0x0100 /* Recursion desired */
#define DRA 0x0080 /* Recursion available */
#define DRCODE 0x000F /* response code, see below */
/* opcode possible values: */
#define DOPQUERY 0 /* a standard query */
#define DOPIQ 1 /* an inverse query */
#define DOPCQM 2 /* a completion query, multiple reply */
#define DOPCQU 3 /* a completion query, single reply */
/* the rest reserved for future */
/* legal response codes: */
#define DROK 0 /* okay response */
#define DRFORM 1 /* format error */
#define DRFAIL 2 /* their problem, server failed */
#define DRNAME 3 /* name error, we know name doesn't exist */
#define DRNOPE 4 /* no can do request */
#define DRNOWAY 5 /* name server refusing to do request */
#define DTYPEA 1 /* host address resource record (RR) */
#define DTYPEPTR 12 /* a domain name ptr */
#define DIN 1 /* ARPA internet class */
#define DWILD 255 /* wildcard for several classifications */
/*
* a resource record is made up of a compressed domain name followed by
* this structure. All of these ints need to be byteswapped before use.
*/
struct rrpart {
word rtype, /* resource record type = DTYPEA */
rclass; /* RR class = DIN */
longword ttl; /* time-to-live, changed to 32 bits */
word rdlength; /* length of next field */
byte rdata[DOMSIZE]; /* data field */
};
/*
* data for domain name lookup
*/
static struct useek {
struct dhead h;
byte x[DOMSIZE];
} *question;
static void
qinit() {
question->h.flags = intel16(DRD);
question->h.qdcount = intel16(1);
question->h.ancount = 0;
question->h.nscount = 0;
question->h.arcount = 0;
}
int
add_server(int *counter, int max, longword *array, longword value)
{
if (array == NULL) return (0); /* failure */
if (value && (*counter < max))
array[ (*counter)++ ] = value;
return (1);
}
/*********************************************************************/
/* packdom
* pack a regular text string into a packed domain name, suitable
* for the name server.
*
* returns length
*/
static int
packdom(dst, src) byte *src, *dst; {
register byte *p, *q;
byte *savedst;
int i, dotflag, defflag;
if (dst == NULL || src == NULL) return (0); /* failure */
dotflag = defflag = 0;
p = src;
savedst = dst;
do { /* copy whole string */
*dst = 0;
q = dst + 1;
while (*p && (*p != '.'))
*q++ = *p++;
i = p - src;
if (i > 0x3f) /* if string is too long */
return (-1);
*dst = i; /* leading length byte */
*q = 0;
if (*p) { /* if a next field */
dotflag = 1; /* say dot seen in string */
src = ++p;
dst = q;
}
else if ((dotflag == 0) && (defflag == 0) && (def_domain != NULL)) {
p = def_domain; /* continue packing with default */
defflag = 1; /* say using default domain ext */
src = p;
dst = q;
}
}
while (*p);
q++;
return (q - savedst); /* length of packed string */
}
/*********************************************************************/
/* unpackdom
* Unpack a compressed domain name that we have received from another
* host. Handles pointers to continuation domain names -- buf is used
* as the base for the offset of any pointer which is present.
* returns the number of bytes at src which should be skipped over.
* Includes the NULL terminator in its length count.
*/
static int
unpackdom(dst, src, buf) byte *src, *dst, buf[]; {
register word i, j;
int retval;
byte *savesrc;
if (src == NULL || dst == NULL || buf == NULL)
return (-1); /* failure */
savesrc = src;
retval = 0;
while (*src) {
j = *src & 0xff; /* length byte */
while ((j & 0xC0) == 0xC0) { /* while 14-bit pointer */
if (retval == 0)
retval = src - savesrc + 2;
src++;
src = &buf[(j & 0x3f)*256+(*src & 0xff)]; /* 14-bit pointer deref */
j = *src & 0xff; /* new length byte */
}
src++; /* assumes 6-bit count */
for (i = 0; i < (j & 0x3f); i++)
*dst++ = *src++; /* copy counted string */
*dst++ = '.'; /* append a dot */
}
*(--dst) = 0; /* add terminator */
src++; /* account for terminator on src */
if (retval == 0)
retval = src - savesrc;
return (retval);
}
/*********************************************************************/
/* sendom
* put together a domain lookup packet and send it
* uses port 53
* num is used as identifier
*/
static word
sendom(s, towho, num) byte *s; longword towho; word num; {
word i,ulen;
register byte *psave;
register byte *p;
psave = question->x;
i = packdom(question->x, s); /* i = length of packed string */
p = &(question->x[i]);
*p++ = 0; /* high byte of qtype */
*p++ = DTYPEA; /* number is < 256, so we know high byte=0 */
*p++ = 0; /* high byte of qclass */
*p++ = DIN; /* qtype is < 256 */
question->h.ident = intel16(num);
ulen = sizeof(struct dhead) + (p - psave);
if (udp_open(dom_sock, 997, towho, 53, NULL) == 0) /* failure */
return (0); /* fail*/
if (sock_write(dom_sock, (byte *)question, ulen) != ulen)
return (0); /* fail */
return (ulen);
}
int countpaths(pathstring) byte *pathstring; {
register int count = 0;
register byte *p;
for (p = pathstring; (*p != 0) || (*(p+1) != 0); p++)
if (*p == '.')
count++;
return (++count);
}
static byte *
getpath(pathstring, whichone)
byte *pathstring; /* the path list to search */
int whichone; /* which path to get, starts at 1 */
{
register byte *retval;
if (pathstring == NULL) return (NULL); /* failure */
if (whichone > countpaths(pathstring))
return (NULL);
whichone--;
for (retval = pathstring; whichone > 0; retval++)
if (*retval == '.')
whichone--;
return (retval);
}
/*********************************************************************/
/* ddextract
* extract the ip number from a response message.
* returns the appropriate status code and if the ip number is available,
* copies it into mip
*/
static longword
ddextract(qp, mip)
struct useek *qp;
byte *mip;
{
register int i;
int j, nans, rcode;
struct rrpart *rrp;
register byte *p;
byte space[260];
if (qp == NULL || mip == NULL) return (0); /* failure */
memset(space, 0, sizeof(space));
nans = intel16(qp->h.ancount); /* number of answers */
rcode = DRCODE & intel16(qp->h.flags); /* return code for this message*/
if (rcode != 0)
return (rcode);
if (nans != 0 && /* at least one answer */
(intel16(qp->h.flags) & DQR)) { /* response flag is set */
p = qp->x; /* where question starts */
if ((i = unpackdom(space,p,qp)) == -1) /* unpack question name */
return (-1); /* failure to unpack */
/* spec defines name then QTYPE + QCLASS = 4 bytes */
p += i + 4;
/*
* at this point, there may be several answers. We will take the first
* one which has an IP number. There may be other types of answers that
* we want to support later.
*/
while (nans-- > 0) { /* look at each answer */
if ((i = unpackdom(space,p,qp)) == -1) /* answer name to unpack */
return (-1); /* failure to unpack */
p += i; /* account for string */
rrp = (struct rrpart *)p; /* resource record here */
/*
* check things which might not align on 68000 chip one byte at a time
*/
if (*p == 0 && *(p+1) == DTYPEA && /* correct type and class */
*(p+2) == 0 && *(p+3) == DIN) {
bcopy(rrp->rdata, mip, 4); /* save IP # */
return (0); /* successful return */
}
bcopy(&rrp->rdlength, &j, 2); /* 68000 alignment */
p += 10 + intel16(j); /* length of rest of RR */
}
}
return (-1); /* generic failed to parse */
}
/*********************************************************************/
/* getdomain
* Look at the results to see if our DOMAIN request is ready.
* It may be a timeout, which requires another query.
*/
static longword
udpdom() {
register int i,uret;
longword desired;
uret = sock_fastread(dom_sock, (byte *)question, sizeof(struct useek));
/* this does not happen */
if (uret < 0) {
/* netputevent(USERCLASS,DOMFAIL,-1); */
return (-1);
}
if (uret == 0) return (0); /* fastread failed to read */
/* check if the necessary information was in the UDP response */
i = ddextract(question, &desired);
switch (i) {
case 3: return (0); /* name does not exist */
case 0: return (intel(desired)); /* we found the IP number */
case -1:return (0); /* strange ret code from ddextract */
default:return (0); /* dunno */
}
}
/**************************************************************************/
/* Sdomain
* DOMAIN based name lookup
* query a domain name server to get an IP number
* Returns the machine number of the machine record for future reference.
* Events generated will have this number tagged with them.
* Returns various negative numbers on error conditions.
*
* if adddom is nonzero, add default domain
*/
static longword
Sdomain(mname, adddom, nameserver, timedout)
byte *mname;
int adddom;
longword nameserver;
int *timedout; /* Set to 1 on timeout */
/* Sdomain */ {
#define NAMBUFSIZ 512
byte namebuff[NAMBUFSIZ];
int namlen;
register int i;
register byte *p;
byte *nextdomain(byte *, int);
longword response;
response = 0;
*timedout = 1; /* presume a timeout */
if (nameserver == 0L)
{ /* no nameserver, give up now */
outs("\r\n No nameserver defined!");
return (0);
}
while (*mname == ' ' || *mname == '\t') mname++;
/* kill leading spaces */
if (*mname == '\0') /* no host name, fail */
return (0L);
qinit(); /* initialize some flag fields */
namlen = strlen(mname); /* Get length of name */
if (namlen >= NAMBUFSIZ || namlen == 0) /* Check it before copying */
return (0L);
strcpy(namebuff, mname); /* OK to copy */
if(namebuff[strlen(namebuff) - 1] == '.')
namebuff[strlen(namebuff) - 1] = '\0';
if (adddom > 0 && adddom <= countpaths(def_domain))
{ /* there is a search list */
p = getpath(def_domain, adddom); /* get end of def_domain */
if (p != NULL) /* if got something */
{
if (strlen(p) > (NAMBUFSIZ - namlen - 1))
return (0L); /* if too big */
if (*p != '.') /* one dot please */
strcat(namebuff, ".");
strcat(namebuff, p); /* new name to try */
}
}
outs("\r\n trying name "); outs(namebuff); /* hand holder */
/*
* This is not terribly good, but it attempts to use a binary
* exponentially increasing delays.
*/
for (i = 2; i < 17; i *= 2)
{
if (sendom(namebuff, nameserver, 0xf001) == 0) /* try UDP */
goto sock_err; /* sendom() failed */
ip_timer_init(dom_sock, i);
do
{
if (tcp_tick(dom_sock) == 0) /* read packets */
goto sock_err; /* socket is closed */
if (ip_timer_expired(dom_sock)) break;
if (sock_dataready(dom_sock)) *timedout = 0;
} while (*timedout);
if (*timedout == 0) break; /* got an answer */
}
if (*timedout == 0) /* if answer, else fall thru*/
{
response = udpdom(); /* process the received data*/
sock_close(dom_sock);
return (response);
}
sock_err:
outs("\r\n Cannot reach name server ");
ntoa(namebuff, nameserver); /* nameserver IP to dotted decimal */
outs(namebuff); /* display nameserver's IP */
*timedout = 1; /* say timeout */
sock_close(dom_sock); /* do for safety's sake */
return (0);
}
/*
* nextdomain - given domain and count = 0,1,2,..., return next larger
* domain or NULL when no more are available
*/
static byte *
nextdomain(byte *domain, int count)
{
register byte *p;
register int i;
if ((p = domain) == NULL) return (NULL); /* failure */
if (count < 0) return (NULL);
for (i = 0; i < count; i++)
{
p = strchr(p, '.');
if (p == NULL) return (NULL);
p++;
}
return (p);
}
static longword
resolve2(byte *name)
{ /* detailed worker for resolve() */
longword ip_address;
register int count;
register int i, j;
byte timeout[MAX_NAMESERVERS];
struct useek qp; /* temp buffer */
udp_Socket ds; /* working socket (big!) */
question = &qp;
dom_sock = &ds;
count = 0;
memset(timeout, 0, sizeof(timeout));
for (;;)
{
for (i = 0; i < last_nameserver; i++)
if (timeout[i] == 0)
if (ip_address = Sdomain(name, count,
def_nameservers[i], &timeout[i]))
return (ip_address);
if ((loc_domain = nextdomain(def_domain, count++)) == NULL)
return (0); /* nothing else to try */
}
return (0);
}
/*
* resolve()
* convert domain name -> address resolution.
* returns 0 if name is unresolvable right now
*/
longword
resolve(name) byte *name; {
if (name == NULL) return (0L);
rip(name); /* terminate on cr and lf's */
if (isaddr(name) != 0)
return (aton(name)); /* IP numerical address */
return (resolve2(name)); /* call upon the worker */
}
/*
* aton()
* - converts [a.b.c.d] or a.b.c.d to 32 bit long
* - returns 0 on error (safer than -1)
*/
longword
aton(text)
byte *text;
{
register int i;
longword ip, j;
ip = 0;
if (text == NULL) return (0L); /* failure */
if (*text == '[')
text++;
for (i = 24; i >= 0; i -= 8)
{
j = atoi(text) & 0xff;
ip |= (j << i);
while (*text != '\0' && *text != '.') text++;
if (*text == '\0')
break;
text++;
}
return (ip);
}
/*
* isaddr
* - returns nonzero if text is simply ip address
* such as 123.456.789.012 or [same] or 123 456 789 012 or [same]
*/
int
isaddr(text)
byte *text;
{
register byte ch;
if (text == NULL) return (0); /* failure */
while (ch = *text++)
{
if (('0' <= ch) && (ch <= '9'))
continue; /* in digits */
if ((ch == '.') || (ch == ' ') || (ch == '[') || (ch == ']'))
continue; /* and in punct */
return (0); /* failure */
}
return (1);
}